﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SudokuState
{
    /// <summary>
    /// Simple web application layer class form managing the state of a sudoku board.
    /// </summary>
    public class SudokuState
    {
        private static int[] CreateEmptyRow()
        {
            return new int[9];
        }

        private static int[][] CreateEmptyGrid()
        {
            return new int[][]{
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow(),
                CreateEmptyRow()
            };
        }

        public const int UNASSIGNED = 0;

        private int[][] grid;

        public int[][] ToArray()
        {
            int[][] result = CreateEmptyGrid();

            for (int row = 0; row < this.grid.Length; row++)
            {
                for (int column = 0; column < this.grid[row].Length; column++)
                {
                    result[row][column] = this.grid[row][column];
                }
            }
            return result;
        }

        public static SudokuState BlankState() {
            return new SudokuState(new int[][]{
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0},
                new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0}
            });
        }

        public static SudokuState ExampleState()
        {
            return new SudokuState(new int[][]{
                new int[]{5, 3, 0, 0, 7, 0, 0, 0, 0},
                new int[]{6, 0, 0, 1, 9, 5, 0, 0, 0},
                new int[]{0, 9, 8, 0, 0, 0, 0, 6, 0},
                new int[]{8, 0, 0, 0, 6, 0, 0, 0, 3},
                new int[]{4, 0, 0, 8, 0, 3, 0, 0, 1},
                new int[]{7, 0, 0, 0, 2, 0, 0, 0, 6},
                new int[]{0, 6, 0, 0, 0, 0, 2, 8, 0},
                new int[]{0, 0, 0, 4, 1, 9, 0, 0, 5},
                new int[]{0, 0, 0, 0, 8, 0, 0, 7, 9}
            });
        }

        public static SudokuState FromMatrix(int[,] _grid)
        {
            int[][] grid = CreateEmptyGrid();
            for (int row = 0; row < 9; row++)
            {
                for (int column = 0; column < 9; column++)
                {
                    grid[row][column] = _grid[column, row];
                }
            }
            return new SudokuState(grid);
        }

        private SudokuState(int[][] _grid)
        {
            this.grid = CreateEmptyGrid();

            for (int row = 0; row < _grid.Length; row++)
            {
                for (int column = 0; column < _grid[row].Length; column++)
                {
                    this.Set(row, column, _grid[row][column]);
                }
            }
        }

        private void CheckColumn(int column)
        {
            CheckArgument(column, "column");
        }

        private void CheckRow(int row)
        {
            CheckArgument(row, "row");
        }

        private void CheckValue(int value)
        {
            CheckArgument(value, "value");
        }

        public void CheckArgument(int arg, string name)
        {
            if (arg < 0)
            {
                throw new ArgumentOutOfRangeException(name, arg, name + " must be greater than 0");
            }
            if (arg > 9)
            {
                throw new ArgumentOutOfRangeException(name, arg, name + " must be less than 10");
            }
        }

        public void Set(int row, int column, int value)
        {

            CheckRow(row);
            CheckColumn(column);
            CheckValue(value);
            this.grid[row][column] = value;
        }

        public int Get(int row, int column)
        {
            CheckRow(row);
            CheckColumn(column);
            return this.grid[row][column];
        }

        public Boolean isSolved()
        {
            foreach (int[] row in this.grid)
            {
                foreach (int x in row)
                {
                    if (x == UNASSIGNED)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }
}
